//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System.Collections;
using System.Reflection;
using System.Text;
using System.Workflow.ComponentModel;
using Microsoft.Research.eResearch.RuntimeServices;
using Microsoft.Research.ScientificWorkflow;
using Wf = System.Workflow.ComponentModel;

namespace Microsoft.Research.eResearch.Execution
{
	/// <summary>
	/// This class is the activity which will be added to each Xoml
    /// file where the activity instance has type providers associated
    /// with it. This activity will deserialize all the initializers
    /// and then assign them to property values.
    /// Note: This is a workaround. Ideall the work that this activity
    /// is doing should be part of the WFExecutor class. However when an
    /// the properties are initialized by using WFInstance returned by
    /// GetWFDefinition they are not being set and hence are null when the
    /// property executes. Need to be investiagated if this is WF 
    /// problem or problem with the way properies are being initialized.
	/// </summary>
    public class Setter: System.Workflow.ComponentModel.Activity
	{
        /// <summary>
        /// Setter activity for instantiating the type providers associated with the
        /// activity instance, and updating the other activities.
        /// </summary>
		public Setter()
		{
		}

        /// <summary>
        /// This activity establishes a connection to the registry and then
        /// loads the activity instance using the Guid set as in I/P. 
        /// This activity then loads all the type initializers associated with
        /// the activiyt instance and then gets values for the objects and assigns
        /// them to the WF parameters.
        /// </summary>
        /// <param name="executionContext"></param>
        /// <returns></returns>
        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            JobExecutionService jobExecServ = executionContext.GetService(typeof(JobExecutionService)) as JobExecutionService;
            if (jobExecServ != null && jobExecServ.ParamsHash != null && jobExecServ.ParamsHash.Count > 0)
            {
                Wf.CompositeActivity rootActivity = executionContext.Activity.Parent;
                Setter.ActivityParametersAssignments(jobExecServ.ParamsHash, rootActivity);
            }

            return ActivityExecutionStatus.Closed;
        }

        /// <summary>
        /// Fetch the key format.
        /// </summary>
        /// <param name="activityName">
        /// The name of the activity.
        /// </param>
        /// <param name="parameterName">
        /// The name of the parameter.
        /// </param>
        /// <returns>
        /// A value using the above 2 params.
        /// </returns>
        private static string FetchActivityParamUniqueKey(string activityName, string parameterName)
        {
            StringBuilder strBuilder = new StringBuilder();
            strBuilder.Append(activityName);
            strBuilder.Append("_");
            strBuilder.Append(parameterName);
            return strBuilder.ToString();
        }

        /// <summary>
        /// Run through all the activities, and foreach, if there is a value available for any input parameter in the passed
        /// hashtable, assign the value.
        /// </summary>
        /// <param name="paramsHash">
        /// The parameters hashtable.
        /// </param>
        /// <param name="activity">
        /// The activity for which to check.
        /// </param>
        private static void ActivityParametersAssignments(Hashtable paramsHash, Wf.Activity activity)
        {
            Setter.UpdateParameter(paramsHash, activity);
            Wf.CompositeActivity compositeActivity = activity as Wf.CompositeActivity;
            if (compositeActivity != null)
            {
                foreach (Wf.Activity childActivity in compositeActivity.Activities)
                {
                    Setter.ActivityParametersAssignments(paramsHash, childActivity);
                }
            }
        }

        /// <summary>
        /// Update the parameters for the activity with the passed hashtable.
        /// </summary>
        /// <param name="paramsHash">
        /// The parameters hashtable.
        /// </param>
        /// <param name="activity">
        /// The activity to be updated.
        /// </param>
        private static void UpdateParameter(Hashtable paramsHash, Wf.Activity activity)
        {
            if (activity != null)
            {
                // Create Activity Parameters
                PropertyInfo[] propertyInfo = activity.GetType().GetProperties();
                foreach (PropertyInfo prop in propertyInfo)
                {
                    object[] inAtts =
                        prop.GetCustomAttributes(typeof(InputParamAttribute), false);
                    if (inAtts.Length == 0)
                    {
                        inAtts = prop.GetCustomAttributes(typeof(InOutParamAttribute), false);
                    }
                    
                    if (inAtts.Length > 0)
                    {
                        string key = Setter.FetchActivityParamUniqueKey(activity.Name, prop.Name);
                        if (paramsHash.ContainsKey(key))
                        {
                            prop.SetValue(activity, paramsHash[key], null);
                        }
                    }
                }
            }
        }        
	}
}
